using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace CGF
{
    [AddComponentMenu("CGFramework/TextEffect")]
    [ExecuteInEditMode]
    public class CTextEffect : BaseMeshEffect
    {
        [HideInInspector] public bool shadowEnable, outlineEnable;
        CText text;
        private static Material shareMaterial;
        private static List<UIVertex> vetexList = new List<UIVertex>();
        private static List<UIVertex> shadowVertices = new List<UIVertex>();

        public override void ModifyMesh(VertexHelper vh)
        {
            if (!IsActive())
            {
                return;
            }

            if (!text) text = GetComponent<CText>();

            //渐变色
            if (text.gradientColor != CText.GradientColor.None)
            {
                Gradient(vh);
            }

            //描边
            if (text.outlines != null && text.outlines.Length > 0)
            {
                Outline(vh);
            }

            //阴影(必须最后渲染，否则不能覆盖颜色)
            if (text.shadows != null && text.shadows.Length > 0)
            {
                Shadow(vh);
            }
        }

        private void Gradient(VertexHelper vh)
        {
            vh.GetUIVertexStream(vetexList);
            int count = vetexList.Count;

            float from = 0;
            float to = 0;

            if (text.gradientColor == CText.GradientColor.Horizontal)
            {
                for (int i = 0; i < count; i++)
                {
                    float x = vetexList[i].position.x;

                    if (x > to)
                    {
                        to = x;
                    }
                    else if (x < from)
                    {
                        from = x;
                    }
                }

                float uiElementWeight = to - from;

                for (int i = 0; i < count; i++)
                {
                    UIVertex uiVertex = vetexList[i];

                    Color color = Color32.Lerp(text.begin, text.end, (uiVertex.position.x - from) / uiElementWeight);
                    color.a = text.color.a;
                    uiVertex.color = color;

                    vetexList[i] = uiVertex;
                }
            }
            else if (text.gradientColor == CText.GradientColor.Vertical)
            {
                for (int i = 0; i < count; i++)
                {
                    float y = vetexList[i].position.y;

                    if (y > from)
                    {
                        from = y;
                    }
                    else if (y < to)
                    {
                        to = y;
                    }
                }

                float uiElementWeight = from - to;

                for (int i = 0; i < count; i++)
                {
                    UIVertex uiVertex = vetexList[i];
                    Color color = Color32.Lerp(text.end, text.begin, (uiVertex.position.y - to) / uiElementWeight);
                    color.a = text.color.a;
                    uiVertex.color = color;
                    vetexList[i] = uiVertex;
                }
            }

            vh.Clear();
            vh.AddUIVertexTriangleStream(vetexList);
        }

        private void Shadow(VertexHelper vh)
        {
            vh.GetUIVertexStream(vetexList);

            shadowVertices.Clear();

            float shadowSize = 0;
            if (text.outlines.Length > 0)
            {
                shadowSize = text.outlines[text.outlines.Length - 1].size * text.fontSize * 0.01f;
            }

            for (int layer = text.shadows.Length - 1; layer >= 0; layer--)
            {
                CText.Shadow shadow = text.shadows[layer];
                int vetexCount = text.text.Trim().Length * 6;
                for (int i = 0; i < vetexCount; i++)
                {
                    if (i < vetexList.Count)
                    {
                        var vetex = vetexList[i];
                        vetex.uv3.x = shadow.color.r;
                        vetex.uv3.y = shadow.color.g;
                        vetex.uv3.z = shadow.color.b;
                        vetex.uv3.w = -1; //标记为处理阴影
                        vetex.uv2.w = shadowSize; //阴影的尺寸
                        vetex.color = new Color(vetex.uv3.x, vetex.uv3.y, vetex.uv3.z, shadow.color.a * text.color.a);
                        vetex.position += new Vector3(shadow.offset.x, shadow.offset.y, 0) * text.fontSize * 0.01f;
                        shadowVertices.Add(vetex);
                    }
                }
            }

            shadowVertices.AddRange(vetexList);

            vh.Clear();
            vh.AddUIVertexTriangleStream(shadowVertices);
        }

        private void Outline(VertexHelper vh)
        {
            vh.GetUIVertexStream(vetexList);

            float meshSize = text.outlines[text.outlines.Length - 1].size * text.fontSize * 0.01f + 10;

            for (int i = 0, count = vetexList.Count - 3; i <= count; i += 3)
            {
                var v1 = vetexList[i];
                var v2 = vetexList[i + 1];
                var v3 = vetexList[i + 2];

                // 计算原顶点坐标中心点
                var minX = _Min(v1.position.x, v2.position.x, v3.position.x);
                var minY = _Min(v1.position.y, v2.position.y, v3.position.y);
                var maxX = _Max(v1.position.x, v2.position.x, v3.position.x);
                var maxY = _Max(v1.position.y, v2.position.y, v3.position.y);
                var posCenter = new Vector2(minX + maxX, minY + maxY) * 0.5f;

                // 计算原始顶点坐标和UV的方向
                Vector2 triX, triY, uvX, uvY;
                Vector2 pos1 = v1.position;
                Vector2 pos2 = v2.position;
                Vector2 pos3 = v3.position;
                if (Mathf.Abs(Vector2.Dot((pos2 - pos1).normalized, Vector2.right))
                    > Mathf.Abs(Vector2.Dot((pos3 - pos2).normalized, Vector2.right)))
                {
                    triX = pos2 - pos1;
                    triY = pos3 - pos2;
                    uvX = v2.uv0 - v1.uv0;
                    uvY = v3.uv0 - v2.uv0;
                }
                else
                {
                    triX = pos3 - pos2;
                    triY = pos2 - pos1;
                    uvX = v3.uv0 - v2.uv0;
                    uvY = v2.uv0 - v1.uv0;
                }

                // 计算原始UV框
                var uvMin = _Min(v1.uv0, v2.uv0, v3.uv0);
                var uvMax = _Max(v1.uv0, v2.uv0, v3.uv0);
                var uvOrigin = new Vector4(uvMin.x, uvMin.y, uvMax.x, uvMax.y);

                // 为每个顶点设置新的Position和UV，并传入原始UV框
                v1 = _SetNewPosAndUV(v1, meshSize, posCenter, triX, triY, uvX, uvY, uvOrigin);
                v2 = _SetNewPosAndUV(v2, meshSize, posCenter, triX, triY, uvX, uvY, uvOrigin);
                v3 = _SetNewPosAndUV(v3, meshSize, posCenter, triX, triY, uvX, uvY, uvOrigin);

                CText.Outline outline = text.outlines[0];

                v1.uv3.x = outline.color.r;
                v1.uv3.y = outline.color.g;
                v1.uv3.z = outline.color.b;
                v1.uv3.w = outline.size * text.fontSize * 0.01f;

                v2.uv3.x = outline.color.r;
                v2.uv3.y = outline.color.g;
                v2.uv3.z = outline.color.b;
                v2.uv3.w = outline.size * text.fontSize * 0.01f;

                v3.uv3.x = outline.color.r;
                v3.uv3.y = outline.color.g;
                v3.uv3.z = outline.color.b;
                v3.uv3.w = outline.size * text.fontSize * 0.01f;

                // 应用设置后的UIVertex
                vetexList[i] = v1;
                vetexList[i + 1] = v2;
                vetexList[i + 2] = v3;
            }

            List<UIVertex> newVetexList = new List<UIVertex>();
            if (text.outlines.Length == 1)
            {
                newVetexList = vetexList;
            }
            else
            {
                for (int layer = text.outlines.Length - 1; layer >= 1; layer--)
                {
                    CText.Outline outline = text.outlines[layer];
                    for (int i = 0; i < vetexList.Count; i++)
                    {
                        var vetex = vetexList[i];
                        vetex.uv3.x = outline.color.r;
                        vetex.uv3.y = outline.color.g;
                        vetex.uv3.z = outline.color.b;
                        vetex.uv3.w = outline.size * text.fontSize * 0.01f;
                        newVetexList.Add(vetex);
                    }
                }

                newVetexList.AddRange(vetexList);
            }

            vh.Clear();
            vh.AddUIVertexTriangleStream(newVetexList);
        }

        public async void Refresh()
        {
            if (!text) text = GetComponent<CText>();
            if (!text) return;
            if (text.outlines.Length > 0)
            {
                if (base.graphic.material != shareMaterial)
                {
                    if (!shareMaterial)
                    {
                        Shader shader;
                        if (Application.isEditor || !CGFConfig.Instance.UseAB)
                        {
                            shader = Shader.Find("CGFramework/Outline");
                        }
                        else if (CGFConfig.Instance.ResStrategy == EResStrategy.Resources)
                        {
                            shader = Resources.Load<Shader>(Define.ShaderFolder + "/Outline");
                        }
                        else
                        {
                            shader = await ResManager.Instance.LoadAsset<Shader>($"{Define.ShaderFolder}/Outline.shader");
                        }
                        shareMaterial = new Material(shader);
                    }

                    base.graphic.material = shareMaterial;
                }

                if (base.graphic.canvas)
                {
                    base.graphic.canvas.additionalShaderChannels |= AdditionalCanvasShaderChannels.TexCoord1 | AdditionalCanvasShaderChannels.TexCoord2 | AdditionalCanvasShaderChannels.TexCoord3;
                    var v1 = base.graphic.canvas.additionalShaderChannels;
                    var v2 = AdditionalCanvasShaderChannels.TexCoord1;
                    if ((v1 & v2) != v2)
                    {
                        base.graphic.canvas.additionalShaderChannels |= v2;
                    }

                    v2 = AdditionalCanvasShaderChannels.TexCoord2;
                    if ((v1 & v2) != v2)
                    {
                        base.graphic.canvas.additionalShaderChannels |= v2;
                    }
                }
            }
            else
            {
                base.graphic.material = null;
            }
        }

        private static UIVertex _SetNewPosAndUV(UIVertex pVertex, float pOutLineWidth,
            Vector2 pPosCenter,
            Vector2 pTriangleX, Vector2 pTriangleY,
            Vector2 pUVX, Vector2 pUVY,
            Vector4 pUVOrigin)
        {
            // Position
            var pos = pVertex.position;
            var posXOffset = pos.x > pPosCenter.x ? pOutLineWidth : -pOutLineWidth;
            var posYOffset = pos.y > pPosCenter.y ? pOutLineWidth : -pOutLineWidth;
            pos.x += posXOffset;
            pos.y += posYOffset;
            pVertex.position = pos;
            // UV
            Vector2 uv = pVertex.uv0;
            uv += pUVX / pTriangleX.magnitude * posXOffset * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1);
            uv += pUVY / pTriangleY.magnitude * posYOffset * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1);
            pVertex.uv0 = uv;
            // 原始UV框
            pVertex.uv1 = new Vector2(pUVOrigin.x, pUVOrigin.y);
            pVertex.uv2 = new Vector2(pUVOrigin.z, pUVOrigin.w);

            return pVertex;
        }

        private static float _Min(float pA, float pB, float pC)
        {
            return Mathf.Min(Mathf.Min(pA, pB), pC);
        }

        private static float _Max(float pA, float pB, float pC)
        {
            return Mathf.Max(Mathf.Max(pA, pB), pC);
        }

        private static Vector2 _Min(Vector2 pA, Vector2 pB, Vector2 pC)
        {
            return new Vector2(_Min(pA.x, pB.x, pC.x), _Min(pA.y, pB.y, pC.y));
        }

        private static Vector2 _Max(Vector2 pA, Vector2 pB, Vector2 pC)
        {
            return new Vector2(_Max(pA.x, pB.x, pC.x), _Max(pA.y, pB.y, pC.y));
        }
    }
}